package com.ilxqx.framework.security.controller;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ilxqx.framework.security.po.PermissionEntry;
import com.ilxqx.framework.security.util.AuthUtils;
import com.ilxqx.framework.system.config.StaticUploadConfigProperties;
import com.ilxqx.framework.system.entity.SysMenu;
import com.ilxqx.framework.system.entity.SysPermission;
import com.ilxqx.framework.system.entity.SysRole;
import com.ilxqx.framework.system.entity.SysUser;
import com.ilxqx.framework.system.service.SysMenuService;
import com.ilxqx.framework.system.service.SysRoleService;
import com.ilxqx.framework.system.service.SysUserService;
import com.ilxqx.framework.util.ResultUtils;
import com.ilxqx.framework.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author venus
 */
@Api("登录控制器")
@Slf4j
@Controller
@RequestMapping("${framework.security.login-processing-url}")
public class LoginController {

    private final SysUserService sysUserService;
	private final SysMenuService sysMenuService;
    private final SysRoleService sysRoleService;

    @Autowired
    public LoginController(SysUserService sysUserService, SysMenuService sysMenuService, SysRoleService sysRoleService) {
        this.sysUserService = sysUserService;
		this.sysMenuService = sysMenuService;
        this.sysRoleService = sysRoleService;
    }

    /**
     * 处理html请求
     * @return 视图文件路径
     */
    @GetMapping(produces = "text/html")
    public String handlePageLogin () {
        return "security/login";
    }

    /**
     * 处理json请求
     * @return vo对象
     */
    @ApiOperation("Ajax请求登录")
    @GetMapping
    @ResponseBody
    public ResultVO handleJsonLogin () {
        return ResultUtils.invalidSession("请使用POST方式请求登录");
    }

    /**
     * 获取用户信息
     * @return VO对象
     */
    @ApiOperation("获取用户信息，包括角色和路由")
    @GetMapping("/getUserInfo")
	@ResponseBody
    public ResultVO getUserInfo() {
        Optional<SysUser> sysUserOptional = this.sysUserService.get(AuthUtils.getLoggedUserId());
        if (sysUserOptional.isEmpty()) {
            log.warn("获取用户信息失败，可能是没有登录或者登录信息失效导致");
            return ResultUtils.failure("无法获取登录用户信息");
        }
        SysUser sysUser = sysUserOptional.get();
        Map<String, Object> data = Maps.newHashMap();
        data.put("name", sysUser.getName());
        data.put("avatar", StaticUploadConfigProperties.domain + sysUser.getAvatar());
        data.put("remark", sysUser.getRemark());
        data.put("roles", sysUser.getUserRoles().stream().map(SysRole::getName).collect(Collectors.toList()));
        data.put("routers", this.getRouters());
        return ResultUtils.success("成功", data);
    }

    @ApiOperation("获取URI的角色")
    @PostMapping("/getUriPermittedRoles")
    @ResponseBody
    public ResultVO getUriRoles(@RequestParam String uris) {
        // uris格式如下
        // uris = "sysRole:getData=/sys/sys-role,get#createData=/sys/sys-role,post|sysUser:getData=/sys/sys-role,get#createData=/sys/sys-role,post"
        Map<String, Map<String, List<String>>> urisPermittedRoles = this.parseUrisAndGetPermittedRoles(uris);
        return ResultUtils.success("成功", urisPermittedRoles);
    }

    private Map<String, Map<String, List<String>>> parseUrisAndGetPermittedRoles(String uris) {
        List<SysRole> sysRoles = this.sysRoleService.getList();
        Map<String, Map<String, List<String>>> moduleData = Maps.newHashMapWithExpectedSize(128);
        String[] modules = StringUtils.split(uris, "|");
        for (String module : modules) {
            // 解析模块
            Map<String, List<String>> actionData = Maps.newHashMap();
            String[] moduleMeta = StringUtils.split(module, ":");
            moduleData.put(moduleMeta[0], actionData);
            Assert.isTrue(moduleMeta.length == 2, "解析出现错误");
            String[] actions = StringUtils.split(moduleMeta[1], "#");
            for (String action : actions) {
                // 解析动作
                String[] actionMeta = StringUtils.split(action, "=");
                Assert.isTrue(actionMeta.length == 2, "解析出现错误");
                String[] info = StringUtils.split(actionMeta[1], ",");
                Assert.isTrue(info.length == 2, "解析出现错误");
                actionData.put(actionMeta[0], this.getUriAcceptedRoles(sysRoles, info[0], info[1]));
            }
        }
        return moduleData;
    }

	/**
	 * 解析路由
	 * @return 路由list
	 */
	private List<SysMenu> getRouters() {
	    List<SysMenu> sysMenuList = this.sysMenuService.getListByParentMenuId(0L);
	    this.sort(sysMenuList);
	    this.fillRoles(sysMenuList, "");
		return sysMenuList;
	}

    /**
     * 填充角色
     * @param sysMenuList 系统菜单列表
     * @param path 初始路径
     */
    private void fillRoles(List<SysMenu> sysMenuList, String path) {
        List<SysRole> sysRoles = this.sysRoleService.getList();
	    sysMenuList.forEach(sysMenu -> {
            List<SysMenu> children = sysMenu.getChildren();
            if (children == null || children.isEmpty()) {
                // 解析权限
                sysMenu.getMeta().setRoles(this.getUriAcceptedRoles(sysRoles, path +
                        (StringUtils.startsWith(sysMenu.getPath(), "/") ? "" : "/") + sysMenu.getPath(), "get"));
                return;
            }
            this.fillRoles(children, path +
                    (StringUtils.startsWith(sysMenu.getPath(), "/") ? "" : "/") + sysMenu.getPath());
            // 判断父级是否应该显示
            List<String> roles = Lists.newArrayListWithCapacity(64);
            for (SysMenu child : children) {
                roles.addAll(child.getMeta().getRoles());
            }
            sysMenu.getMeta().setRoles(Lists.newArrayList(Sets.newHashSet(roles)));
        });
    }

    /**
     * 获取Uri允许的角色
     * @param uri 请求URI
     * @param method 请求方法
     * @return 允许的角色
     */
    private List<String> getUriAcceptedRoles(List<SysRole> sysRoles, String uri, String method) {
        List<String> roles = Lists.newArrayList();
        sysRoles.forEach(sysRole -> {
            Set<SysPermission> sysPermissions = sysRole.getRolePermissions();
            for (SysPermission sysPermission : sysPermissions) {
                PermissionEntry permissionEntry = new PermissionEntry();
                permissionEntry.setUris(Arrays.asList(StringUtils.split(sysPermission.getUri(), ",")));
                permissionEntry.setMethod(sysPermission.getMethod());
                permissionEntry.setDataOperationRange(sysRole.getDataOperationRange());
                if (AuthUtils.uriIsMatch(uri, method, permissionEntry)) {
                    roles.add(sysRole.getName());
                    break;
                }
            }
        });
        return roles;
    }

    /**
     * 菜单排序
     * @param sysMenuList 菜单列表
     */
	private void sort(List<SysMenu> sysMenuList) {
	    sysMenuList.sort(Comparator.comparingInt(SysMenu::getSequence));
	    sysMenuList.forEach(sysMenu -> {
	        if (sysMenu.getChildren() != null) {
	            this.sort(sysMenu.getChildren());
            }
        });
    }

    @Data
    private static class UriEntry {

	    private String uri;

	    private String method;

    }

}
